package btree4j;

import btree4j.BTree.BTreeNode;
import btree4j.indexer.BasicIndexQuery;
import btree4j.indexer.IndexQuery;
import btree4j.utils.datetime.StopWatch;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.*;
import java.util.concurrent.Callable;

public class BTreeStructurePrinter {

    private static final int NUM_ENTRIES = 10000;
    private static final int NUM_QUERIES = 1000;
    private static final int WARMUP_ITERATIONS = 1000;
    private static final int MEASUREMENT_ITERATIONS = 1000;
    private static final Random RANDOM = new Random();

    public static void printTreeToFile(BTree tree, String filename) throws Exception {
        try (BufferedWriter writer = new BufferedWriter(new FileWriter(filename))) {
            BTreeNode root = tree.getRootNode();
            Set<Long> visited = new HashSet<>();
            printNode(tree, root, writer, 0, visited);
        }
    }

    private static void printNode(BTree tree, BTreeNode node, BufferedWriter writer,
                                  int level, Set<Long> visited) throws Exception {
        long pageNum = node.page.getPageNum();
        if (visited.contains(pageNum)) {
            writer.write(getIndent(level) + "Node #" + pageNum + " [CIRCULAR REFERENCE]\n");
            return;
        }
        visited.add(pageNum);

        // 打印节点基本信息
        String nodeType = node.ph.getStatus() == BTree.LEAF ? "LEAF" : "BRANCH";
        writer.write(getIndent(level) + String.format(
                "Node #%d [%s] Parent:%d Prev:%d Next:%d\n",
                pageNum, nodeType, node.ph.parentPage, node.prev, node.next
        ));

        // 打印键值
        writer.write(getIndent(level) + "Keys: ");
        for (Value key : node.keys) {
            writer.write(new String(key.getData()) + " ");
        }
        writer.newLine();

        // 打印指针
        writer.write(getIndent(level) + "Pointers: ");
        for (long ptr : node.ptrs) {
            writer.write(ptr + " ");
        }
        writer.newLine();

        // 递归打印子节点
        if (node.ph.getStatus() == BTree.BRANCH) {
            for (int i = 0; i < node.ptrs.length; i++) {
                BTreeNode child = tree.getChildNode(node, i);
                printNode(tree, child, writer, level + 1, visited);
            }
        }

        // 增加分支节点验证
        if (node.ph.getStatus() == BTree.BRANCH) {
            if (node.ptrs.length != node.keys.length + 1) {
                writer.write(getIndent(level) + "INVALID BRANCH: keys=" + node.keys.length
                        + " ptrs=" + node.ptrs.length + "\n");
            }
            // 增加路由键验证
            for (int i = 0; i < node.keys.length - 1; i++) {
                if (node.keys[i].compareTo(node.keys[i+1]) >= 0) {
                    writer.write(getIndent(level) + "UNSORTED KEYS: [" + i + "]="
                            + new String(node.keys[i].getData()) + " > [" + (i+1) + "]="
                            + new String(node.keys[i+1].getData()) + "\n");
                }
            }
        }
    }

    private static String getIndent(int level) {
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < level; i++) {
            sb.append("|  ");
        }
        return sb.toString();
    }



    public static void main(String[] args) throws Exception {
        File file = new File("btree_test.idx");
        BTree btree = new BTree(file);
        btree.init(false);

        // 插入测试数据
        // 修改main方法中的插入逻辑
        List<Value> keys = new ArrayList<>();
        for (int i = 0; i < 100000; i++) {
            String key = String.format("key%04d", i); // 补零保证排序正确
            Value keyValue = new Value(key.getBytes());
            keys.add(keyValue);
            btree.addValue(keyValue, i);
        }

        // 打印树结构
        printTreeToFile(btree, "btree_structure_afterinsert.txt");
        System.out.println("B+树结构已保存到 btree_structure_afterinsert.txt");

//        // 预热缓存
//        for (Value key : keys) {
//            btree.findValue(key);
//        }
//
//        // 预热 JVM JIT 编译器
//        warmUpJIT(btree, keys);
//
//        // 等值查询性能测试
//        testEqualityQueries(btree, keys);
//
//        // 范围查询性能测试
//        testRangeQueriesL(btree, keys);
//        testRangeQueries(btree, keys);


        // 删除测试数据
        for (int i = 0; i < 300; i++) {
            String key = String.format("key%04d", i);
            Value keyValue = new Value(key.getBytes());
            btree.removeValue(keyValue);
        }

        // 打印删除后的树结构
        printTreeToFile(btree, "btree_structure_afterdelete.txt");
        System.out.println("删除操作后的 B+ 树结构已保存到 btree_structure_afterdelete.txt");

        // 关闭 BTree
        btree.close();

    }

    private static void warmUpJIT(BTree btree, List<Value> keys) {
        // 预热 JVM JIT 编译器
        for (int i = 0; i < WARMUP_ITERATIONS; i++) {
            Value key = keys.get(RANDOM.nextInt(keys.size()));
            try {
                btree.findValue(key);
            } catch (BTreeException e) {
                e.printStackTrace();
            }
        }
    }

    private static void testEqualityQueries(BTree btree, List<Value> keys) {
        // 选择左侧、右侧和中间的键
        Value leftKey = keys.get(0);
        Value rightKey = keys.get(keys.size() - 1);
        Value middleKey = keys.get(keys.size() / 2);

        // 测试左侧键查询
        long leftTime = measureQueryTime(() -> {
            return btree.findValue(leftKey);
        }, MEASUREMENT_ITERATIONS);
        System.out.printf("左侧键查询平均耗时: %.2f ns%n", (double) leftTime / MEASUREMENT_ITERATIONS);

        // 测试右侧键查询
        long rightTime = measureQueryTime(() -> {
            return btree.findValue(rightKey);
        }, MEASUREMENT_ITERATIONS);
        System.out.printf("右侧键查询平均耗时: %.2f ns%n", (double) rightTime / MEASUREMENT_ITERATIONS);

        // 测试中间键查询
        long middleTime = measureQueryTime(() -> {
            return btree.findValue(middleKey);
        }, MEASUREMENT_ITERATIONS);
        System.out.printf("中间键查询平均耗时: %.2f ns%n", (double) middleTime / MEASUREMENT_ITERATIONS);
    }

    private static void testRangeQueriesL(BTree btree, List<Value> keys) {
        // 选择中间键作为范围查询的起点
        Value endKey = keys.get(keys.size() / 2);
        Value startKey = keys.get(keys.size() / 2 - 100);

        // 构建范围查询条件
        IndexQuery rangeQuery = new BasicIndexQuery(BasicIndexQuery.BW, startKey, endKey);

        // 测试范围查询
        long rangeTime = measureQueryTime(() -> {
            BTreeCallback callback = new BTreeCallback() {
                @Override
                public boolean indexInfo(Value value, long pointer) {
                    return true;
                }

                @Override
                public boolean indexInfo(Value key, byte[] value) {
                    return true;
                }
            };
            btree.search(rangeQuery, callback);
            return 0L; // 返回一个值以满足 Callable<Long> 的要求
        }, MEASUREMENT_ITERATIONS);

        System.out.printf("左侧范围查询平均耗时: %.2f ns%n", (double) rangeTime / MEASUREMENT_ITERATIONS);
    }

    private static void testRangeQueries(BTree btree, List<Value> keys) {
        // 选择中间键作为范围查询的起点
        Value startKey = keys.get(keys.size() / 2);
        Value endKey = keys.get(keys.size() / 2 + 100);

        // 构建范围查询条件
        IndexQuery rangeQuery = new BasicIndexQuery(BasicIndexQuery.BW, startKey, endKey);

        // 测试范围查询
        long rangeTime = measureQueryTime(() -> {
            BTreeCallback callback = new BTreeCallback() {
                @Override
                public boolean indexInfo(Value value, long pointer) {
                    return true;
                }

                @Override
                public boolean indexInfo(Value key, byte[] value) {
                    return true;
                }
            };
            btree.search(rangeQuery, callback);
            return 0L; // 返回一个值以满足 Callable<Long> 的要求
        }, MEASUREMENT_ITERATIONS);

        System.out.printf("右侧范围查询平均耗时: %.2f ns%n", (double) rangeTime / MEASUREMENT_ITERATIONS);
    }

    private static long measureQueryTime(Callable<Long> queryCallable, int iterations) {
        long totalTime = 0;
        for (int i = 0; i < iterations; i++) {
            long start = System.nanoTime();
            try {
                queryCallable.call();
            } catch (Exception e) {
                e.printStackTrace();
            }
            long end = System.nanoTime();
            totalTime += (end - start);
        }
        return totalTime;
    }

    private static List<Value> getRandomKeys(List<Value> keys, int count) {
        List<Value> result = new ArrayList<>();
        for (int i = 0; i < count; i++) {
            result.add(keys.get(RANDOM.nextInt(keys.size())));
        }
        return result;
    }
}
